%{
#include <stdio.h>
#include <iostream>

#include "Ast.hh"

using namespace std;

// Prototypes to lexer functions
extern void yyerror (const char *error);
extern int  yylex ();

// interface to the outside world
extern list<Entity *> *toplevel;  // list of top-level classes
extern EntityTable *global_symtab; // global symbol table
extern int yylineno;

Type *varType = NULL;
char *currClassName = NULL;

%}


%token TOK_BOOLEAN TOK_BREAK TOK_CLASS TOK_CONTINUE TOK_ELSE 
        TOK_EXTENDS TOK_FALSE TOK_FLOAT TOK_FOR TOK_IF TOK_INT TOK_NEW 
        TOK_NULL TOK_PRIVATE TOK_PUBLIC TOK_RETURN TOK_STATIC TOK_SUPER
        TOK_THIS TOK_TRUE TOK_VOID TOK_WHILE TOK_DO
        TOK_INT_CONST TOK_FLOAT_CONST TOK_STRING_CONST 
        TOK_ID 
        TOK_COMMA TOK_DOT TOK_SEMICOLON
		TOK_OPEN_SQ_BRACKET TOK_CLOSE_SQ_BRACKET
        TOK_OPEN_PAREN TOK_CLOSE_PAREN TOK_OPEN_BRACE TOK_CLOSE_BRACE 
        TOK_PLUS TOK_MINUS TOK_MULTIPLY TOK_DIVIDE 
        TOK_PLUS_PLUS TOK_MINUS_MINUS TOK_EQUAL TOK_AND TOK_OR TOK_NOT 
        TOK_GREATER TOK_LESSER TOK_EQUAL_EQUAL TOK_NOT_EQUAL
        TOK_GREATER_OR_EQUAL TOK_LESSER_OR_EQUAL
        TOK_SYNTAX_ERROR TOK_RUNAWAY_STRING

%union{
        char* string_val;
        int   int_val;
        float float_val;
        char char_val;
		bool bool_val;
		Entity *entity;
		Type *type;
		Statement *stmt;
		Expression *expr;
		list<Entity*> *entity_list;
		list<Statement*> *stmt_list;
		list<Expression*> *expr_list;
		struct Modifier {
			bool optVisibility;
			bool optStatic; 
		} modifier;
		struct MethodHead {
			struct Modifier modifier;
			Type *type;
			char *name;
		}methodHead;
}

%right TOK_EQUAL
%left TOK_OR
%left TOK_AND
%left TOK_EQUAL_EQUAL TOK_NOT_EQUAL 
%nonassoc TOK_LESSER TOK_GREATER TOK_LESSER_OR_EQUAL TOK_GREATER_OR_EQUAL
%left TOK_PLUS TOK_MINUS 
%left TOK_MULTIPLY TOK_DIVIDE
%left TOK_NOT 

%type <entity_list> ClassDeclarations
%type <entity_list> FormalsOpt
%type <entity_list> FormalParamCommaList
%type <entity_list> ClassBodyDecls
%type <entity> FormalParam
%type <entity> ClassDeclaration
%type <entity> Variable
%type <entity> ClassBodyDecl
%type <entity> FieldDecl
%type <entity> MethodDecl
%type <entity> ConstructorDecl
%type <string_val> ExtendsOpt
%type <bool_val> VisibilityOpt
%type <bool_val> StaticOpt
%type <modifier> Modifier
%type <methodHead> MethodHead
%type <stmt> Block
%type <stmt_list> StmtStar
%type <stmt> Stmt
%type <type> Type
%type <int_val> DimStar 
%type <string_val> TOK_ID
%type <expr> Expr
%type <stmt> OptElsePart
%type <stmt> StmtExprOpt
%type <entity_list> VarDecl
%type <entity_list> Variables
%type <entity_list> VariablesCommaList
%type <expr> StmtExpr
%type <expr> ExprOpt
%type <expr> Assignment
%type <expr> MethodInvocation
%type <expr> LeftHandSide
%type <expr> FieldAccess
%type <expr> ArrayAccess
%type <expr> DimExpr
%type <expr> Primary
%type <expr> Literal
%type <expr_list> DimExprPlus  
%type <expr_list> CommaExprStar
%type <expr_list> ArgumentListOpt 
%type <int_val> TOK_INT_CONST
%type <float_val> TOK_FLOAT_CONST
%type <string_val> TOK_STRING_CONST
%type <bool_val> TOK_TRUE
%type <bool_val> TOK_FALSE

/*****
Define the type of attribute values for grammar symbols here.

Examples:
%type <int_val> TOK_INT_CONST
The above declarations say that TOK_INT_CONST's attribute value is in the 
int_val field of YYSTYPE (defined by the "union" earlier); 
VisibilityOpt's attribute value is in bool_val field of YYSTYPE; 
and the attribute value of ClassDeclarations is in the entity_list field 
of YYSTYPE

*****/

%%

/**/
/***************************** DECLARATIONS  ********************************/
/**/

/* push_front for right recursive grammer rules.
	 push_back for left  recursive productions.
*/


Program : { global_symtab->enter_block(); }  ClassDeclarations { global_symtab->leave_block(); }  {
	/* In the action for this production, set 
	the global variable "toplevel" to the list of entities representing 
	the classes (i.e. the attribute of ClassDeclarations).
	*/
	toplevel = $2;
	}
	;

ClassDeclarations:
	  ClassDeclarations ClassDeclaration { 
		$$ = $1;
		$$->push_back($2);
		} 
        | {
			$$ = new list<Entity *>();
		}
	;

ClassDeclaration:
	TOK_CLASS TOK_ID 
	ExtendsOpt {     
		bool current = false;
		Entity *entity = global_symtab->find_entity($2, CLASS_ENTITY, &current);  		
		if( NULL != entity )
		{
			cout << "Error: "<< yylineno << " : class " << $2 << " redeclared" << endl;
			exit(0);
		}
		/* To check if a class can inherit itself */
		Entity *super = NULL;
		if ( NULL != $3 ) {
			current = false;
			super = global_symtab->find_entity($3, CLASS_ENTITY, &current);
			if( !strcmp($3, $2) )
			{
				cout << "Error: " << yylineno << " : class " << $2 << " cannot inherit itself" << endl;
				exit(0);
			}
			if( NULL == super )
			{
				cout << "Error: " << yylineno << " : super class " << $3 << " not found" << endl;
				exit(0);
			}
		}
		currClassName = $2;
		new ClassEntity($2, super, NULL);
	}
	TOK_OPEN_BRACE
		{ 
				global_symtab->enter_block();
		}
	ClassBodyDecls
		{ 
				global_symtab->leave_block(); 
		}
	TOK_CLOSE_BRACE {
			bool current = false;
			Entity *entity = global_symtab->find_entity($2, CLASS_ENTITY, &current);
			if( NULL != dynamic_cast<ClassEntity *>(entity) )
			{
				dynamic_cast<ClassEntity *>(entity)->set_class_members($7);
				$$ = entity;
			}
		} 
		;

ExtendsOpt:
	  TOK_EXTENDS TOK_ID { $$ = $2; } 
        | { $$ = NULL; } 
	;

ClassBodyDecls:
	  ClassBodyDecls ClassBodyDecl {
		$$ = $1;
		if( $2 != NULL )
			$$->push_back($2);	  
	  }	
	| ClassBodyDecl {
		if ( NULL != $1 )
		{
			$$ = new list<Entity *>();
			$$->push_back($1);
		}
	  }	
	;

ClassBodyDecl:	
	    FieldDecl { $$ = $1; }
	  	| MethodDecl { $$ = $1; }
        | ConstructorDecl { $$ = $1; }
	  ;

FieldDecl: Modifier Type TOK_ID DimStar TOK_SEMICOLON {
				bool current = false;
				Entity *entity = NULL;
                entity = global_symtab->find_entity($3,
                                            FIELD_ENTITY, &current);
				if( NULL == entity )	
					$$ = new FieldEntity($3, $1.optVisibility, $1.optStatic, $2, $4);
		   		else
				{
					cout << "Error: " << yylineno << " : field " << $3 << " redeclaration in class " << currClassName << endl;
					$$ = NULL;
					exit(0);
				}
			}
	 ;
Modifier: VisibilityOpt StaticOpt { 
			$$.optVisibility = $1;
			$$.optStatic = $2;
		}
	 ;

VisibilityOpt : 
          TOK_PUBLIC { $$ = true; }
        | TOK_PRIVATE { $$ = false; }
        | { $$ = true; }
	;

StaticOpt:
          TOK_STATIC { $$ = true; }
        | { $$ = false; }
	;

VarDecl: Type { varType = $1;}
		Variables TOK_SEMICOLON { $$ = $3; }
	 ;

Type:	  TOK_INT { $$ = new IntType(); }
	| TOK_FLOAT	{ $$ = new FloatType(); }
	| TOK_BOOLEAN { $$ = new BooleanType(); } 
	| TOK_ID {
		Entity *entity = NULL;
       	bool current = false;
        entity = global_symtab->find_entity($1, CLASS_ENTITY, 
											&current);
		if( NULL == entity )
		{
			cout << "Error: " << yylineno << " : class " << $1 << " not found" << endl; 
			exit(0);
		}
		$$ = new ClassType(entity);
	} 
	;

Variables: Variable VariablesCommaList { $$ = $2; $$->push_back($1); } 
	  ;

Variable: TOK_ID DimStar {
					bool current = false;
					Entity *entity = global_symtab->find_entity($1, VARIABLE_ENTITY, &current);
					if( ( NULL != entity ) && ( true == current ) ) {
						cout << "Error: " << yylineno << " : variable " << $1 << " redeclared" << endl;
						exit(0);
					}
					$$ = new VariableEntity($1, varType, $2);
			}
	  ;

VariablesCommaList:
	  TOK_COMMA Variable VariablesCommaList { $$ = $3; $$->push_back($2); }
        | { $$ = new list<Entity*>(); }
	;

MethodDecl:
	  MethodHead TOK_OPEN_PAREN { global_symtab->enter_block(); } 
	  FormalsOpt TOK_CLOSE_PAREN Block 
	  {  global_symtab->leave_block(); }  {
		if( !strcmp($1.name, currClassName) ) {
			cout << "Error: " << yylineno <<" : constructor in class " << currClassName << " should not have a return type" << endl;
			exit(0);
		}
		$$ = new MethodEntity($1.name, $1.modifier.optVisibility, $1.modifier.optStatic,
							  $1.type, $4, $6);
	  }
	  ;

MethodHead: Modifier Type TOK_ID {
			$$.modifier = $1;
			$$.type = $2;
			$$.name = $3;
		}
         | Modifier TOK_VOID TOK_ID {
			$$.modifier = $1;
			$$.type = new VoidType();
			$$.name = $3;
		}
	 ;

FormalsOpt:
	  FormalParam FormalParamCommaList { $$ = $2; $$->push_front($1); }  
        | { $$ = new list<Entity *>(); }
	;

FormalParam: Type { varType = $1; } Variable { $$ = $3; } 
	;

FormalParamCommaList: 
	  TOK_COMMA FormalParam FormalParamCommaList { $$ = $3; $$->push_front($2); } 
        | { $$ = new list<Entity *>(); } 
	;

ConstructorDecl:
	 Modifier TOK_ID TOK_OPEN_PAREN { global_symtab->enter_block(); } 
	 FormalsOpt TOK_CLOSE_PAREN Block 
	 { global_symtab->leave_block(); } {
		if( strcmp($2, currClassName) ) {
			cout << "Error: " << yylineno <<" : function " << $2 << " in class " << currClassName << " should have a return type" << endl;
			exit(0);
		}		
		$$ = new ConstructorEntity($2, $1.optVisibility, $5, $7); 
	 }
	 ;

/**/
/*****************************   STATEMENTS ********************************/
/**/


Block:	  TOK_OPEN_BRACE StmtStar TOK_CLOSE_BRACE { $$ = new BlockStatement($2); }	
	  ;

StmtStar:
	  Stmt StmtStar { $$ = $2; $$->push_front($1); }
	| { $$ = new list<Statement *>(); }
	;


Stmt: TOK_IF TOK_OPEN_PAREN Expr TOK_CLOSE_PAREN Stmt OptElsePart {
		$$ = new IfStatement($3, $5, $6);	
	}
	| TOK_WHILE TOK_OPEN_PAREN Expr TOK_CLOSE_PAREN Stmt {
		$$ = new WhileStatement($3, $5);	
	}
	| TOK_FOR TOK_OPEN_PAREN StmtExprOpt
	                    TOK_SEMICOLON ExprOpt
			    TOK_SEMICOLON StmtExprOpt
		TOK_CLOSE_PAREN Stmt {
		$$ = new ForStatement($3, $5, $7, $9);
	}
	| TOK_RETURN Expr TOK_SEMICOLON {
		$$ = new ReturnStatement($2);
	}
	| { global_symtab->enter_block(); } Block { global_symtab->leave_block(); } {
		$$ = $2;
	}
	| StmtExpr TOK_SEMICOLON {
		$$ = new ExprStatement($1); 
	}
	| VarDecl {
		$$ = new DeclStatement($1);
	}
	| TOK_BREAK TOK_SEMICOLON {
		$$ = new BreakStatement();
	}
	| TOK_CONTINUE TOK_SEMICOLON {
		$$ = new ContinueStatement();
	}
	| TOK_SEMICOLON {
		$$ = new SkipStatement();
	}
	| error TOK_SEMICOLON 
	  /* Error production to synchronize at SEMICOLON on any parse error */
	;

OptElsePart:
	  TOK_ELSE Stmt { $$ = $2; }
	| { $$ = new SkipStatement(); }
	;

StmtExprOpt:
	  StmtExpr { $$ = new ExprStatement($1); }
	| { $$ = new SkipStatement(); }
	;

ExprOpt:
	  Expr { $$ = $1; }
	| { $$ = new NullExpression(); }
	;

StmtExpr:  Assignment { $$ = $1; }
	| MethodInvocation { $$ = $1; }
	;

/**/
/*****************************   EXPRESSIONS ********************************/
/**/


Primary:  Literal { $$ = $1; }
	| TOK_THIS { $$ = new ThisExpression(); }
	| TOK_SUPER { $$ = new SuperExpression(); } 
	| TOK_OPEN_PAREN Expr TOK_CLOSE_PAREN { $$ = $2; } 
	| TOK_NEW TOK_ID TOK_OPEN_PAREN ArgumentListOpt TOK_CLOSE_PAREN { 
			bool current = false;
			Entity *entity = global_symtab->find_entity($2, CLASS_ENTITY, &current);
			if( NULL == entity ) {
					cout << "Error: " << yylineno << " : class " << $2 << " not defined" << endl;
					exit(0);
			}
			$$ = new NewInstance(entity, $4); } 
	| LeftHandSide { $$ = $1; }
	| MethodInvocation { $$ = $1; }

ArgumentListOpt: 	
	  Expr CommaExprStar { $$ = $2; $$->push_front($1); } 
	| { $$ = new list<Expression *>(); $$->push_front(new NullExpression()); }
	;

CommaExprStar: TOK_COMMA Expr CommaExprStar { $$ = $3; $$->push_front($2); }
	| { $$ = new list<Expression *>(); }
	;

FieldAccess:
	  Primary TOK_DOT TOK_ID { $$ = new FieldAccess($1, $3); }
	| TOK_ID { 
				bool current = false;	
				Expression *expr = NULL;
				Entity *entity = global_symtab->find_entity($1, VARIABLE_ENTITY, &current);
				if( NULL == entity ) {	
					entity = global_symtab->find_entity($1, FIELD_ENTITY, &current);
					if( NULL == entity )
					{
						entity = global_symtab->find_entity($1, CLASS_ENTITY, &current);
					}
				}
				if( NULL == entity )
					expr = new FieldAccess(new ThisExpression(), $1);
				else
					expr = new IdExpression(entity);
				$$ = expr;
	}
	;
ArrayAccess: Primary TOK_OPEN_SQ_BRACKET Expr TOK_CLOSE_SQ_BRACKET { $$ = new ArrayAccess( $1, $3); } 
	;

MethodInvocation:
	  Primary TOK_DOT TOK_ID TOK_OPEN_PAREN ArgumentListOpt TOK_CLOSE_PAREN {
	  		$$ = new MethodInvocation($1, $3, $5);
	  }	
	|
	  TOK_ID TOK_OPEN_PAREN ArgumentListOpt TOK_CLOSE_PAREN {
			$$ = new MethodInvocation(new ThisExpression(), $1, $3);
	}
	;

Expr:	  Expr TOK_MULTIPLY Expr { $$ = new BinaryExpression(MUL, $1, $3); }	
	| Expr TOK_DIVIDE Expr { $$ = new BinaryExpression(DIV, $1, $3); }
	| Expr TOK_PLUS Expr { $$ = new BinaryExpression(ADD, $1, $3); }	
	| Expr TOK_MINUS Expr { $$ = new BinaryExpression(SUB, $1, $3); }	
	| Expr TOK_AND Expr	{ $$ = new BinaryExpression(AND, $1, $3); }
	| Expr TOK_OR Expr { $$ = new BinaryExpression(OR, $1, $3); }	
	| Expr TOK_EQUAL_EQUAL Expr { $$ = new BinaryExpression(EQ, $1, $3); }
	| Expr TOK_NOT_EQUAL Expr { $$ = new BinaryExpression(NEQ, $1, $3); }	
	| Expr TOK_LESSER Expr { $$ = new BinaryExpression(LT, $1, $3); }		
	| Expr TOK_GREATER Expr { $$ = new BinaryExpression(GT, $1, $3); }		
	| Expr TOK_LESSER_OR_EQUAL Expr { $$ = new BinaryExpression(LEQ, $1, $3); }	
	| Expr TOK_GREATER_OR_EQUAL Expr  { $$ = new BinaryExpression(GEQ, $1, $3); }
	| TOK_MINUS Expr	%prec TOK_NOT { $$ = new UnaryExpression(UMINUS, $2); }
	| TOK_PLUS Expr		%prec TOK_NOT { $$ = $2; }	 
	| TOK_NOT Expr { $$ = new UnaryExpression(NEG, $2); }
	| Primary { $$ = $1; }
	| Assignment { $$ = $1; }  
	| TOK_NEW Type DimExprPlus DimStar { $$ = new NewArrayInstance($2, $3->size() + $4, $3); } 
	;	

DimExprPlus:
	  DimExprPlus DimExpr { $$ = $1; $$->push_back($2); }
	| DimExpr { $$ = new list<Expression *>(); $$->push_back($1); }
	;

DimExpr:  TOK_OPEN_SQ_BRACKET Expr TOK_CLOSE_SQ_BRACKET { $$ = $2; }
	  ;

DimStar:  Dim DimStar { $$ = 1 + $2; }
        | { $$ = 0; }
	;

Dim:	  TOK_OPEN_SQ_BRACKET  TOK_CLOSE_SQ_BRACKET ;


Assignment:
	  LeftHandSide TOK_EQUAL Expr { $$ = new AssignExpression($1, $3); }
	| LeftHandSide TOK_PLUS_PLUS { $$ = new AutoExpression(POST_INCR, $1); }
	| LeftHandSide TOK_MINUS_MINUS { $$ = new AutoExpression(POST_DECR, $1); } 
	| TOK_PLUS_PLUS LeftHandSide { $$ = new AutoExpression(PRE_INCR, $2); }
	| TOK_MINUS_MINUS LeftHandSide { $$ = new AutoExpression(PRE_DECR, $2); }
	;

LeftHandSide:
	  FieldAccess { $$ = $1; }
	| ArrayAccess { $$ = $1; }
	;

Literal:  TOK_INT_CONST { $$ = new IntegerConstant($1); }
	| TOK_FLOAT_CONST { $$ = new FloatConstant($1); }
	| TOK_STRING_CONST { $$ = new StringConstant($1); }
	| TOK_NULL { $$ = new NullExpression(); }
	| TOK_TRUE { $$ = new BooleanConstant($1); } 
	| TOK_FALSE { $$ = new BooleanConstant($1); }
	;

%%
